import pytest
import requests
from unittest.mock import patch

# 定义测试数据
TEST_INSTANCES = [
    {
        "InstanceType": "S5.SMALL1",
        "ImageId": "img-31tjrtph",
        "InstanceChargeType": "POSTPAID_BY_HOUR",
        "SecurityGroupIds": ["sg-123456"],
        "VpcId": "vpc-abcdef",
        "SubnetId": "subnet-123456",
        "InstanceName": "test-instance"
    },
    {
        "InstanceType": "S5.LARGE2",
        "ImageId": "img-987654",
        "InstanceChargeType": "PREPAID",
        "SecurityGroupIds": ["sg-abcdef"],
        "VpcId": "vpc-123456",
        "SubnetId": "subnet-abcdef",
        "InstanceName": "prepaid-instance",
        "InternetAccessible": {
            "InternetChargeType": "TRAFFIC_POSTPAID_BY_HOUR",
            "InternetMaxBandwidthOut": 100
        }
    }
]


# 定义全局fixture
@pytest.fixture(scope="module")
def api_url():
    return "https://cvm.tencentcloudapi.com"


@pytest.fixture
def headers():
    return {
        "Content-Type": "application/json",
        "X-TC-Action": "CreateInstances",
        "Authorization": "TC3-HMAC-SHA256 Credential=AKIDEXAMPLE/2023-01-01/cvm/tc3_request"
    }


# 正常用例
def test_create_instance_success(mocker, headers, api_url):
    mock_response = {
        "Response": {
            "InstanceIdSet": ["ins-123456"],
            "RequestId": "req-123456"
        }
    }

    with patch.object(requests, 'post') as mock_post:
        mock_post.return_value.status_code = 200
        mock_post.return_value.json.return_value = mock_response

        response = requests.post(api_url, headers=headers, json=TEST_INSTANCES[0])

        assert response.status_code == 200
        assert "InstanceIdSet" in response.json()["Response"]
        assert len(response.json()["Response"]["InstanceIdSet"]) == 1


# 异常场景测试
def test_missing_required_parameters(mocker, headers, api_url):
    invalid_payload = TEST_INSTANCES[0].copy()
    del invalid_payload["InstanceType"]

    with patch.object(requests, 'post') as mock_post:
        mock_post.return_value.status_code = 400
        mock_post.return_value.json.return_value = {
            "Response": {
                "Error": {
                    "Code": "MissingParameter",
                    "Message": "InstanceType is required"
                }
            }
        }

        response = requests.post(api_url, headers=headers, json=invalid_payload)

        assert response.status_code == 400
        assert response.json()["Response"]["Error"]["Code"] == "MissingParameter"


def test_invalid_image_id(mocker, headers, api_url):
    invalid_payload = TEST_INSTANCES[0].copy()
    invalid_payload["ImageId"] = "invalid-image-id"

    with patch.object(requests, 'post') as mock_post:
        mock_post.return_value.status_code = 400
        mock_post.return_value.json.return_value = {
            "Response": {
                "Error": {
                    "Code": "InvalidImageId.NotFound",
                    "Message": "The specified image does not exist."
                }
            }
        }

        response = requests.post(api_url, headers=headers, json=invalid_payload)

        assert response.status_code == 400
        assert "InvalidImageId.NotFound" in response.json()["Response"]["Error"]["Code"]


def test_insufficient_quota(mocker, headers, api_url):
    invalid_payload = TEST_INSTANCES[0].copy()
    invalid_payload["InstanceChargeType"] = "PREPAID"
    invalid_payload["InstanceCount"] = 100  # 超过默认配额

    with patch.object(requests, 'post') as mock_post:
        mock_post.return_value.status_code = 400
        mock_post.return_value.json.return_value = {
            "Response": {
                "Error": {
                    "Code": "LimitExceeded.InstanceQuota",
                    "Message": "Instance quota exceeded."
                }
            }
        }

        response = requests.post(api_url, headers=headers, json=invalid_payload)

        assert response.status_code == 400
        assert "LimitExceeded.InstanceQuota" in response.json()["Response"]["Error"]["Code"]


def test_invalid_network_config(mocker, headers, api_url):
    invalid_payload = TEST_INSTANCES[0].copy()
    invalid_payload["VpcId"] = "invalid-vpc-id"

    with patch.object(requests, 'post') as mock_post:
        mock_post.return_value.status_code = 400
        mock_post.return_value.json.return_value = {
            "Response": {
                "Error": {
                    "Code": "InvalidVpcId.NotFound",
                    "Message": "The specified VPC does not exist."
                }
            }
        }

        response = requests.post(api_url, headers=headers, json=invalid_payload)

        assert response.status_code == 400
        assert "InvalidVpcId.NotFound" in response.json()["Response"]["Error"]["Code"]


# 参数化测试
@pytest.mark.parametrize("instance_type, charge_type", [
    ("S5.SMALL1", "POSTPAID_BY_HOUR"),
    ("S5.LARGE2", "PREPAID"),
    ("CVM.S5.SMALL1", "POSTPAID_BY_HOUR")  # 部分匹配测试
])
def test_instance_type_variations(mocker, headers, api_url, instance_type, charge_type):
    payload = TEST_INSTANCES[0].copy()
    payload["InstanceType"] = instance_type
    payload["InstanceChargeType"] = charge_type

    with patch.object(requests, 'post') as mock_post:
        mock_post.return_value.status_code = 200
        mock_post.return_value.json.return_value = {
            "Response": {
                "InstanceIdSet": ["ins-123456"],
                "RequestId": "req-123456"
            }
        }

        response = requests.post(api_url, headers=headers, json=payload)

        assert "InstanceIdSet" in response.json()["Response"]


@pytest.mark.parametrize("security_group_ids", [
    ["sg-123456"],
    ["sg-123456", "sg-abcdef"],
    []
])
def test_security_group_variations(mocker, headers, api_url, security_group_ids):
    payload = TEST_INSTANCES[0].copy()
    payload["SecurityGroupIds"] = security_group_ids

    with patch.object(requests, 'post') as mock_post:
        mock_post.return_value.status_code = 200
        mock_post.return_value.json.return_value = {
            "Response": {
                "InstanceIdSet": ["ins-123456"],
                "RequestId": "req-123456"
            }
        }

        response = requests.post(api_url, headers=headers, json=payload)

        assert "InstanceIdSet" in response.json()["Response"]


# 边界条件测试
def test_zero_instance_count(mocker, headers, api_url):
    payload = TEST_INSTANCES[0].copy()
    payload["InstanceCount"] = 0

    with patch.object(requests, 'post') as mock_post:
        mock_post.return_value.status_code = 400
        mock_post.return_value.json.return_value = {
            "Response": {
                "Error": {
                    "Code": "InvalidParameter",
                    "Message": "InstanceCount cannot be zero."
                }
            }
        }

        response = requests.post(api_url, headers=headers, json=payload)

        assert response.status_code == 400
        assert "InvalidParameter" in response.json()["Response"]["Error"]["Code"]


def test_max_instance_count(mocker, headers, api_url):
    payload = TEST_INSTANCES[0].copy()
    payload["InstanceCount"] = 100  # 接近配额上限

    with patch.object(requests, 'post') as mock_post:
        mock_post.return_value.status_code = 200
        mock_post.return_value.json.return_value = {
            "Response": {
                "InstanceIdSet": ["ins-123456"] * 100,
                "TotalCount": 100,
                "RequestId": "req-123456"
            }
        }

        response = requests.post(api_url, headers=headers, json=payload)

        assert len(response.json()["Response"]["InstanceIdSet"]) == 100


# 异步任务测试
def test_async_operation(mocker, headers, api_url):
    payload = TEST_INSTANCES[0].copy()
    payload["DryRun"] = True

    with patch.object(requests, 'post') as mock_post:
        mock_post.return_value.status_code = 200
        mock_post.return_value.json.return_value = {
            "Response": {
                "AsyncRequestId": "async-req-123456",
                "RequestId": "req-123456"
            }
        }

        response = requests.post(api_url, headers=headers, json=payload)

        assert "AsyncRequestId" in response.json()["Response"]